﻿using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.Practices.Unity;
using MyAMS.WebServer.ActionFilter;

namespace MyAMS.WebServer
{
    internal sealed class ModulesManager : BaseComponent, IModulesManager
    {
        private readonly IUnityContainer unityContainer;
        private readonly List<IModule> modules;

        public ModulesManager(IUnityContainer unityContainer)
        {
            this.unityContainer = unityContainer;
            EnvironmentLoader = unityContainer.Resolve<IEnvironmentLoader>();
            modules = new List<IModule>(unityContainer.ResolveAll<IModule>());
            foreach (IMiddleware middleware in unityContainer.ResolveAll<IMiddleware>())
            {
                modules.Add(middleware);
            }
        }

        #region IModulesManager Members

        public IEnvironmentLoader EnvironmentLoader { get; private set; }

        public IEnumerable<IModule> Modules
        {
            get { return modules; }
        }

        public int ModuleCount
        {
            get { return modules.Count; }
        }

        public void Load(IModule module)
        {
            if (!module.IsLoaded && EnvironmentLoader.IsLoaded)
            {
                if (module is IMiddleware && !(module as IMiddleware).IsLoadable(this))
                {
                    return;
                }
                module.OnLoading();
                RouteCollection routes = unityContainer.Resolve<RouteCollection>();
                IEnumerable<IModule> loadedModules = Modules.Where(m => m.IsLoaded);
                foreach (IModule m in loadedModules)
                {
                    m.UnLoadRoutes(routes);
                }

                module.LoadIgnoredRoutes(routes);
                module.LoadRoutes(routes);
                foreach (IModule m in loadedModules)
                {
                    m.LoadIgnoredRoutes(routes);
                    m.LoadRoutes(routes);
                }

                module.LoadCatchAllRoutes(routes);
                foreach (IModule m in loadedModules)
                {
                    m.LoadCatchAllRoutes(routes);
                }

                module.LoadViewEngines(unityContainer.Resolve<ViewEngineCollection>());
                module.LoadActionFilters(unityContainer.Resolve<ActionFilterRegistry>());
                module.LoadModelBinders(unityContainer.Resolve<ModelBinderDictionary>());
                module.OnLoaded();
            }
        }

        public void UnLoad(IModule module)
        {
            if (module.IsLoaded)
            {
                module.UnLoad();
            }
        }

        #endregion

        protected override void OnInitialize()
        {
            LoadEnvironment();

            LoadModules();

            LoadMiddlewares();

            // 此为最后一个路由, 表示未找到任何适合的Controll和Action
            unityContainer.Resolve<RouteCollection>().MapRoute(
                    "Final",
                    "{*anything}",
                    new { controller = "Home", action = "NotFound" },
                    new { },
                    new string[] { "MyAMS.WebServer.Controllers" }
                    );
        }

        protected override void OnDispose(bool isDisposing)
        {
            unityContainer.Resolve<RouteCollection>().Clear();

            UnLoadMiddlewares();

            UnLoadModules();

            UnLoadEnvironment();
        }

        private void LoadEnvironment()
        {
            if (!EnvironmentLoader.IsLoaded)
            {
                EnvironmentLoader.Load();
            }
        }

        private void UnLoadEnvironment()
        {
            if (EnvironmentLoader.IsLoaded)
            {
                EnvironmentLoader.UnLoad();
            }
        }

        private void LoadModules()
        {
            if (EnvironmentLoader.IsLoaded)
            {
                IEnumerable<IModule> modules = GetModules();
                foreach (IModule module in modules)
                {
                    module.OnLoading();
                }
                RouteCollection routes = unityContainer.Resolve<RouteCollection>();
                foreach (IModule module in modules)
                {
                    module.LoadIgnoredRoutes(routes);
                    module.LoadRoutes(routes);
                    module.LoadViewEngines(unityContainer.Resolve<ViewEngineCollection>());
                    module.LoadActionFilters(unityContainer.Resolve<ActionFilterRegistry>());
                    module.LoadModelBinders(unityContainer.Resolve<ModelBinderDictionary>());
                }
                foreach (IModule module in modules)
                {
                    module.LoadCatchAllRoutes(routes);
                }

                foreach (IModule module in modules)
                {
                    module.OnLoaded();
                }
            }
        }

        private void UnLoadModules()
        {
            foreach (IModule module in GetModules())
            {
                if (module.IsLoaded)
                {
                    module.UnLoad();
                }
            }
        }

        private void LoadMiddlewares()
        {
            if (EnvironmentLoader.IsLoaded)
            {
                IEnumerable<IMiddleware> middlewares = GetMiddlewares();
                foreach (IMiddleware middleware in middlewares)
                {
                    if (middleware.IsLoadable(this))
                    {
                        middleware.OnLoading();
                    }
                }
                RouteCollection routes = unityContainer.Resolve<RouteCollection>();
                foreach (IMiddleware middleware in middlewares)
                {
                    if (middleware.IsLoadable(this))
                    {
                        middleware.LoadIgnoredRoutes(routes);
                        middleware.LoadRoutes(routes);
                        middleware.LoadViewEngines(unityContainer.Resolve<ViewEngineCollection>());
                        middleware.LoadActionFilters(unityContainer.Resolve<ActionFilterRegistry>());
                        middleware.LoadModelBinders(unityContainer.Resolve<ModelBinderDictionary>());
                    }
                }
                foreach (IMiddleware middleware in middlewares)
                {
                    if (middleware.IsLoadable(this))
                    {
                        middleware.LoadCatchAllRoutes(routes);
                    }
                }

                foreach (IMiddleware middleware in middlewares)
                {
                    if (middleware.IsLoadable(this))
                    {
                        middleware.OnLoaded();
                    }
                }
            }
        }

        private void UnLoadMiddlewares()
        {
            foreach (IMiddleware coordinator in GetMiddlewares())
            {
                if (coordinator.IsLoaded)
                {
                    coordinator.UnLoad();
                }
            }
        }

        private IEnumerable<IMiddleware> GetMiddlewares()
        {
            return from m in Modules
                   where m is IMiddleware && !m.IsLoaded
                   select m as IMiddleware;
        }

        private IEnumerable<IModule> GetModules()
        {
            return Modules.Where(m => !(m is IMiddleware) && !m.IsLoaded);
        }
    }
}
